#include "device.h"
#include "../mds/sys/string.h"

static
char * ocl_device_string(cl_device_id   id,
						 cl_device_info name)
{

	char * s      = 0;
	size_t length = 0;
	
	if( CL_SUCCESS != ( ocl_errno = clGetDeviceInfo(id, name, 0, NULL, &length ) ) )
	{
		return NULL;
	}
	assert(length>0);
	
	if( NULL == ( s = mds_string_create( length-1 ) ) )
	{
		return NULL;
	}
	
	if( CL_SUCCESS != ( ocl_errno =  clGetDeviceInfo(id, name, length, s, NULL) ) )
	{
		mds_string_delete(s);
		return NULL;
	}
	
	return s;
}


int  ocl_device_load( cl_device_id id, ocl_device *device )
{
	static const char fn[] = "[ocl_device_load]";
	ocl_errno = CL_SUCCESS;
	assert( device );
	memset( device, 0, sizeof(ocl_device) );
	device->id = id;
	
#define OCL_DEVICE_INFO(FIELD,TYPE) \
if( CL_SUCCESS != ( ocl_errno = clGetDeviceInfo(device->id, CL_DEVICE_##FIELD, sizeof(TYPE), & device->FIELD, NULL) ) ) goto ERROR; else \
if( ocl_logfile ) fprintf( ocl_logfile, "%s |_%-20s = %lu\n", fn, #FIELD, (unsigned long)(device->FIELD) )

#define OCL_DEVICE_INFO_STRING(FIELD,NAME) if( NULL == ( device->FIELD = ocl_device_string( device->id, NAME) ) ) goto ERROR; else \
if( ocl_logfile ) fprintf( ocl_logfile, "%s |_%-20s = '%s'\n", fn, #FIELD, device->FIELD )
	
	OCL_DEVICE_INFO(TYPE,cl_device_type);
	OCL_DEVICE_INFO(MAX_COMPUTE_UNITS,cl_uint);
	if( CL_SUCCESS != ( ocl_errno = clGetDeviceInfo( device->id, CL_DEVICE_MAX_WORK_ITEM_SIZES, 3*sizeof(size_t), device->MAX_WORK_ITEM_SIZES, NULL) ) )
	{
		goto ERROR;
	}
	if( ocl_logfile )
	{
		unsigned k=0;
		for( ; k <3 ; ++k )
		{
			fprintf( ocl_logfile, "%s |_%-20s[%u] = %lu\n", fn, "MAX_WORK_ITEM_SIZES", k, (unsigned long)(device->MAX_WORK_ITEM_SIZES[k]) );
		}
	}
	OCL_DEVICE_INFO(MAX_WORK_GROUP_SIZE,size_t);
	OCL_DEVICE_INFO(MAX_CLOCK_FREQUENCY,cl_uint);
	OCL_DEVICE_INFO(MAX_CONSTANT_BUFFER_SIZE,cl_ulong);
	OCL_DEVICE_INFO(MAX_MEM_ALLOC_SIZE,cl_ulong);
	OCL_DEVICE_INFO(GLOBAL_MEM_SIZE,cl_ulong);
	OCL_DEVICE_INFO(LOCAL_MEM_SIZE,cl_ulong);
	OCL_DEVICE_INFO(PLATFORM,cl_platform_id);
	OCL_DEVICE_INFO_STRING(NAME,CL_DEVICE_NAME); 
	OCL_DEVICE_INFO_STRING(VENDOR,CL_DEVICE_VENDOR); 
	OCL_DEVICE_INFO_STRING(DRIVER_VERSION,CL_DRIVER_VERSION);
	OCL_DEVICE_INFO_STRING(OPENCL_VERSION,CL_DEVICE_VERSION);
	
	return 0;
ERROR:
	ocl_device_quit(device);
	return -1;
}

void ocl_device_quit( ocl_device *device )
{
	static const char fn[] = "[ocl_device_quit]";
	assert(device);
	if( ocl_logfile ) fprintf( ocl_logfile, "%s\n", fn);
	mds_string_delete( device->OPENCL_VERSION);
	mds_string_delete( device->DRIVER_VERSION);
	mds_string_delete( device->VENDOR );
	mds_string_delete( device->NAME   );
	
	memset( device, 0, sizeof(ocl_device) );
}

cl_bool ocl_device_available( const ocl_device *device )
{
	cl_bool AVAILABLE = CL_FALSE;
	assert( device );
	if( CL_SUCCESS != clGetDeviceInfo(device->id, CL_DEVICE_AVAILABLE, sizeof(cl_bool), &AVAILABLE, NULL) )
	{
		return CL_FALSE;
	}
	
	return AVAILABLE;
}

cl_bool ocl_device_can_compile( const ocl_device *device )
{
	cl_bool COMPILER_AVAILABLE = CL_FALSE;
	assert( device );
	if( CL_SUCCESS != clGetDeviceInfo(device->id, CL_DEVICE_COMPILER_AVAILABLE, sizeof(cl_bool), &COMPILER_AVAILABLE, NULL) )
	{
		return CL_FALSE;
	}
	
	return COMPILER_AVAILABLE;
}

size_t  ocl_device_max_alloc_real4( const ocl_device *device )
{
	assert( device );
	return device->MAX_MEM_ALLOC_SIZE / sizeof(Real4);
}

size_t ocl_device_max_local_real4( const ocl_device *device )
{
	assert( device );
	return device->LOCAL_MEM_SIZE / sizeof(Real4);
}
